#include <iostream>
#include <pthread.h>
#include <unistd.h>
using namespace std;


#if 0           // version 1：全局定义型

pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;        // 【互斥锁使用：第一步】：定义互斥锁，并使用宏定义方式对其初始化

int tickets = 100;                      // 全局共享资源：未加保护，多线程执行，可能导致数据紊乱操作

// 如果多线程访问同一个全局变量，并对它进行数据计算，多线程会互相影响吗？
// 答：会；在并发访问的时候，导致了数据的不一致！

void *Tickets(void* args){
    while(true){

        /* 到此为止，多线程可以并行，即：多个线程可以同时执行到此，以下语句执行后，只允许串行 */
        pthread_mutex_lock( &mtx );     // 【互斥锁使用：第二步】：加锁【对共享数据实现保护】
        /* 以上代码确保了：在任意时刻只允许一个线程对以下代码进行执行！故：此处会发生阻塞！ */

        if(tickets > 0){                // 判断的本质也是一种计算
        /* 计算：就是把数据从内存读取到CPU的寄存器中，本质：就是把数据读取到执行流的上下文中 */
            usleep(1000);               // 微秒等待
            cout << "Thread " << (char*)args << " sells ticket : " << tickets << endl;
            tickets--;                  // 也可能出现问题

            pthread_mutex_unlock( &mtx );   // 【互斥锁使用：第三步】：解锁【对共享数据实现保护】
        }
        else{
            pthread_mutex_unlock( &mtx );   // 【互斥锁使用：第三步】：解锁【对共享数据实现保护】
            break;
        }
        /*
            说明：
                1. 在加锁与解锁执行的代码，就是：临界区！
                2. 被共享的全局数据，就是：临界资源！
                3. 加锁粒度越小越好！【加锁一定会引起效率问题！】
        */

    }
    pthread_exit( (void*)10 );

}


int main(){

    pthread_t t1, t2, t3, t4;
    pthread_create( &t1, nullptr, Tickets, (void*)"1");
    pthread_create( &t2, nullptr, Tickets, (void*)"2");
    pthread_create( &t3, nullptr, Tickets, (void*)"3");
    pthread_create( &t4, nullptr, Tickets, (void*)"4");

    pthread_join( t1, nullptr);
    pthread_join( t2, nullptr);
    pthread_join( t3, nullptr);
    pthread_join( t4, nullptr);

    return 0;
}

#else            // version 2：局部定义型【注意传参方式！】


#define PTHREAD_NUM 5

int tickets = 10000;

class ThreadData{
public:
    ThreadData(const string &n, pthread_mutex_t *p)
        :t_name(n),p_mtx(p)
        {}
public:
    string t_name;
    pthread_mutex_t *p_mtx;

};


void* Tickets(void* args){
    ThreadData *td = (ThreadData*)args;

    while(1){
        // 第三步：加锁：
        pthread_mutex_lock( td->p_mtx );
        if(tickets > 0){
            usleep(1000);               
            cout << td->t_name << " sells ticket : " << tickets << endl;
            tickets--;   
            // 第四步：解锁：
            pthread_mutex_unlock( td->p_mtx );    
        }else{
            // 第四步：解锁：
            pthread_mutex_unlock( td->p_mtx );    
            break;
        }
    }
    return nullptr;
}


int main(){

    pthread_mutex_t mtx;                    // 第一步：定义局部互斥锁
    pthread_mutex_init( &mtx, nullptr );    // 第二步：初始化互斥锁【后续注意销毁！】

    pthread_t t[PTHREAD_NUM];

    // 多线程抢票逻辑

    for(int i = 0;i < PTHREAD_NUM;i++){
        // 构造名字：
        string name = "Thread ";
        name += to_string(i+1);

        // 构造对象
        ThreadData *td = new ThreadData(name, &mtx);

        pthread_create( t+i, nullptr, Tickets, (void*)td);
    }

    for(int i = 0;i < PTHREAD_NUM;i++){

        pthread_join( t[i], nullptr );

    }

    pthread_mutex_destroy( &mtx );      // 第五步：销毁互斥锁！

    return 0;
}



#endif